package com.ruoyi.common.service.impl;

import com.aliyun.oss.ClientException;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.mapper.AOssMapper;
import com.ruoyi.common.domain.AOss;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.tencent.cloud.CosStsClient;
import com.tencent.cloud.Policy;
import com.tencent.cloud.Response;
import com.tencent.cloud.Statement;
import com.tencent.cloud.cos.util.Jackson;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.common.service.IAOssService;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * oss配置管理Service业务层处理
 *
 * @author ruoyi
 * @date 2024-05-16
 */
@Service
@Transactional
public class AOssServiceImpl implements IAOssService {
    @Autowired
    private AOssMapper aOssMapper;

    /**
     * 查询oss配置管理
     *
     * @param id oss配置管理主键
     * @return oss配置管理
     */
    @Override
    public AOss selectAOssById(Long id) {
        return aOssMapper.selectAOssById(id);
    }

    @Override
    public AOss getCurrentOss() {
        return aOssMapper.getCurrentOss();
    }

    /**
     * 查询oss配置管理列表
     *
     * @param aOss oss配置管理
     * @return oss配置管理
     */
    @Override
    public List<AOss> selectAOssList(AOss aOss) {
        return aOssMapper.selectAOssList(aOss);
    }

    /**
     * 新增oss配置管理
     *
     * @param aOss oss配置管理
     * @return 结果
     */
    @Override
    public int insertAOss(AOss aOss) {
        aOss.setCreateTime(DateUtils.getNowDate());
        if (aOss.getApply()) {
             AOss oss = getCurrentOss();
             if (ObjectUtils.isNotEmpty(oss)){
                 setDefault(oss.getId(), false);
             }
        }
        return aOssMapper.insertAOss(aOss);
    }

    /**
     * 修改oss配置管理
     *
     * @param aOss oss配置管理
     * @return 结果
     */
    @Override
    public int updateAOss(AOss aOss) {
        AOss currentOss = getCurrentOss();
        // 设置为使用时
        if (aOss.getApply()) {
            // 将当前使用oss 设置为不使用
            currentOss.setApply(false);
            aOssMapper.updateAOss(currentOss);
        } else {
            // oss设置为不使用, 如果当前使用的是这个oss 进行处理
            if (currentOss.getId().equals(aOss.getId())) {
                // 如果是本地oss
                if (aOss.getId() == 1L) {
                    // 不允许设置为不使用
                    aOss.setApply(true);
                } else {
                    // 将本地oss 设置为使用状态
                    setDefault(1L, true);
                }
            }
        }
        // 更新当前oss信息
        aOss.setUpdateTime(DateUtils.getNowDate());
        return aOssMapper.updateAOss(aOss);
    }

    /**
     * 批量删除oss配置管理
     *
     * @param ids 需要删除的oss配置管理主键
     * @return 结果
     */
    @Override
    public int deleteAOssByIds(Long[] ids) {
        AOss aOss = aOssMapper.getCurrentOss();
        for (Long id : ids) {
            if (aOss.getId().equals(id)) {
                setDefault(1L, true);
            }
        }
        return aOssMapper.deleteAOssByIds(ids);
    }

    private void setDefault(Long id, boolean apply) {
        // 将本地oss 设置为使用状态
        AOss localOss = aOssMapper.selectAOssById(id);
        localOss.setApply(apply);
        aOssMapper.updateAOss(localOss);
    }

    /**
     * 删除oss配置管理信息
     *
     * @param id oss配置管理主键
     * @return 结果
     */
    @Override
    public int deleteAOssById(Long id) {
        AOss aOss = aOssMapper.getCurrentOss();
        if (aOss.getId().equals(id)) {
            setDefault(1L, true);
        }
        return aOssMapper.deleteAOssById(id);
    }

    @Override
    public Map<String, Object> getCosSTS() {
        HashMap<String, Object> res = new HashMap<>();
        //查询腾讯云配置
        AOss oss = getCurrentOss();
        if(!StringUtils.equals(oss.getCurrentType(), "1")){
            throw new ServiceException("请开启腾讯云存储");
        }
        String appId = oss.getAppId().trim();
        String secretId = oss.getAppKey().trim();
        String secretKey = oss.getAppSecret().trim();
        String bucket = oss.getBucket().trim();
        String region = oss.getRegion().trim();
        try {
            TreeMap<String, Object> config = new TreeMap<>();
            //SecretId,SecretKey为用于申请临时密钥的永久身份(主账号、子账号等),且子账号需要具有操作存储桶的权限
            config.put("secretId", secretId);
            config.put("secretKey", secretKey);

            // 初始化 policy
            Policy policy = new Policy();

            // 设置域名:
            // 如果您使用了腾讯云 cvm，可以设置内部域名
            //config.put("host", "sts.internal.tencentcloudapi.com");

            // 临时密钥有效时长，单位是秒，默认 1800 秒，目前主账号最长 2 小时（即 7200 秒），子账号最长 36 小时（即 129600）秒
            config.put("durationSeconds", 1800);
            config.put("bucket", bucket);
            config.put("region", region);
            // 开始构建一条 statement
            Statement statement = new Statement();
            // 声明设置的结果是允许操作
            statement.setEffect("allow");
            statement.addActions(new String[]{
                    "cos:PutObject",
                    // 表单上传、小程序上传
                    "cos:PostObject",
                    // 分块上传
                    "cos:InitiateMultipartUpload",
                    "cos:ListMultipartUploads",
                    "cos:ListParts",
                    "cos:UploadPart",
                    "cos:CompleteMultipartUpload",
                    // 处理相关接口一般为数据万象产品 权限中以ci开头
                    // 创建媒体处理任务
                    "ci:CreateMediaJobs",
                    // 文件压缩
                    "ci:CreateFileProcessJobs"
            });
            statement.addResources(new String[]{
                    "qcs::cos:"+ region +":uid/"+ appId +":"+ bucket +"/*",
                    "qcs::ci:"+ region +":uid/"+ appId +":bucket/"+ bucket +"/*"});
            // 把一条 statement 添加到 policy,可以添加多条
            policy.addStatement(statement);
            // 将 Policy 示例转化成 String，可以使用任何 json 转化方式，这里是 cos SDK 自带的推荐方式
            config.put("policy", Jackson.toJsonPrettyString(policy));
            Response response = CosStsClient.getCredential(config);
            res.put("tmpSecretId", response.credentials.tmpSecretId);
            res.put("tmpSecretKey", response.credentials.tmpSecretKey);
            res.put("sessionToken", response.credentials.sessionToken);
            // 编码（base64字符串）
            byte[] bytes = Jackson.toJsonPrettyString(policy).getBytes();
            String policyBase64 = Base64.getEncoder().encodeToString(bytes);
            res.put("policy", policyBase64);
            res.put("bucket", bucket);
            res.put("region", region);
            res.put("startTime", response.startTime);
            res.put("expiredTime", response.expiredTime);
        } catch (Exception e) {
            e.printStackTrace();
            throw new IllegalArgumentException("no valid secret !");
        }
        return res;
    }

    @Override
    public Map<String, Object>  getOssSTS() {
        HashMap<String, Object> res = new HashMap<>();
        //查询腾讯云配置
        AOss oss = getCurrentOss();
        //断言替换
        if(!StringUtils.equals(oss.getCurrentType(), "0")){
            throw new ServiceException("请开启阿里云存储");
        }
        String appId = oss.getAppId();
        String accessKeyId = oss.getAppKey();
        String accessKeySecret = oss.getAppSecret();
        String url = oss.getUrl();
        String bucket = oss.getBucket();
        String region = oss.getRegion();

        // STS服务接入点，例如sts.cn-hangzhou.aliyuncs.com。您可以通过公网或者VPC接入STS服务。
        String endpoint = "sts."+ region +".aliyuncs.com";
        // 从环境变量中获取步骤3生成的RAM角色的RamRoleArn。
        String roleArn = "todo";
        // 自定义角色会话名称，用来区分不同的令牌，例如可填写为SessionTest。
        String roleSessionName = "zhanhui";
        // 临时访问凭证将获得角色拥有的所有权限。
        String policy = null;
        // 临时访问凭证的有效时间，单位为秒。最小值为900，最大值以当前角色设定的最大会话时间为准。当前角色最大会话时间取值范围为3600秒~43200秒，默认值为3600秒。
        // 在上传大文件或者其他较耗时的使用场景中，建议合理设置临时访问凭证的有效时间，确保在完成目标任务前无需反复调用STS服务以获取临时访问凭证。
        Long durationSeconds = 3600L;
        try {
            // 发起STS请求所在的地域。建议保留默认值，默认值为空字符串（""）。
            String regionId = "";
            // 添加endpoint。适用于Java SDK 3.12.0及以上版本。
            DefaultProfile.addEndpoint(regionId, "Sts", endpoint);
            // 添加endpoint。适用于Java SDK 3.12.0以下版本。
            // DefaultProfile.addEndpoint("",regionId, "Sts", endpoint);
            // 构造default profile。
            IClientProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
            // 构造client。
            DefaultAcsClient client = new DefaultAcsClient(profile);
            final AssumeRoleRequest request = new AssumeRoleRequest();
            // 适用于Java SDK 3.12.0及以上版本。
            request.setSysMethod(MethodType.POST);
            // 适用于Java SDK 3.12.0以下版本。
            // request.setMethod(MethodType.POST);
            request.setRoleArn(roleArn);
            request.setRoleSessionName(roleSessionName);
            request.setPolicy(policy);
            request.setDurationSeconds(durationSeconds);
            final AssumeRoleResponse response = client.getAcsResponse(request);
            res.put("tmpSecretId", response.getCredentials().getAccessKeyId());
            res.put("tmpSecretKey", response.getCredentials().getAccessKeySecret());
            res.put("sessionToken", response.getCredentials().getSecurityToken());
            res.put("bucket", bucket);
            res.put("region", region);
        } catch (ClientException e) {
            System.out.println("Failed：");
            System.out.println("Error code: " + e.getErrorCode());
            System.out.println("Error message: " + e.getErrorMessage());
            System.out.println("RequestId: " + e.getRequestId());
        } catch (ServerException e) {
            throw new RuntimeException(e);
        } catch (com.aliyuncs.exceptions.ClientException e) {
            throw new RuntimeException(e);
        }
        return res;
    }
}
